package com.epam.epamtest.service;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.*;
import cn.hutool.json.JSONObject;
import com.epam.epamtest.config.RateLimit;
import com.epam.epamtest.config.UrlProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static com.epam.epamtest.config.Constants.*;

@Service
@Slf4j
public class TemperatureService {

    private final static Lock lock = new ReentrantLock();

    @Autowired
    UrlProperties urlProperties;

    @PostConstruct
    public void initProvinces() {
        JSONObject provinces = getEntities(urlProperties.getProvincesUrl(), true, true);
        log.info("provinces:{}", provinces);
    }

    @RateLimit
    @Retryable(value = Exception.class, exclude = IllegalArgumentException.class,
            maxAttempts = MAX_ATTEMPTS, backoff = @Backoff(delay = 2000, multiplier = 1, maxDelay = 10000))
    public Optional<Integer> getTemperature(String province, String city, String county) {
        Assert.notBlank(province, ERROR_MSG_TEMPLATE, "province");
        Assert.notBlank(city, ERROR_MSG_TEMPLATE, "city");
        Assert.notBlank(county, ERROR_MSG_TEMPLATE, "county");

//        log.info("province:{},city:{},county:{}", province, city, county);

        try {
            lock.lock();
            String key = province + city + county;
            Integer cache = (Integer) TIMED_CACHE.get(key);
            if (cache != null) {
                return Optional.of(cache);
            }

            JSONObject provinces = (JSONObject) TIMED_CACHE.get("");

            JSONObject cities = getEntities(urlProperties.getCitiesUrl(), true, true, provinces.get(province));
//        log.info("cities:{}", cities);
            if (cities.containsKey(STATUS)) {
                return Optional.of(cities.getInt(STATUS));
            }

            JSONObject countries = getEntities(urlProperties.getCountiesUrl(), true, true, provinces.get(province), cities.get(city));
//        log.info("countries:{}", countries);
            if (countries.containsKey(STATUS)) {
                return Optional.of(countries.getInt(STATUS));
            }

            JSONObject weather;

            //如果是直辖市，拼接url的市和区的入参是相反的
            if (MUNICIPALITY_SET.contains(province)) {
                weather = getEntities(urlProperties.getWeatherUrl(), false, false, provinces.get(province), countries.get(county), cities.get(city));
            } else {
                weather = getEntities(urlProperties.getWeatherUrl(), false, false, provinces.get(province), cities.get(city), countries.get(county));
            }
            log.info("weather:{}", weather);
            if (weather.containsKey(STATUS)) {
                return Optional.of(weather.getInt(STATUS));
            }

            Integer temp = weather.getJSONObject("weatherinfo").getInt("temp");
            TIMED_CACHE.put(key, temp);
            return Optional.of(temp);
        } finally {
            lock.unlock();
        }
    }

    public JSONObject getEntities(String url, boolean inverse, boolean cache, Object... params) {
        JSONObject jsonObject = new JSONObject();

        //校验非法入参，如果存在null，说明入参有误
        for (int i = 0; i < params.length; i++) {
            if (params[i] == null) {
                jsonObject.set(STATUS, ERROR_CODE);
                jsonObject.set("info", PARAM_NAME[i] + "参数有误");
                return jsonObject;
            }
        }

        //获取缓存数据
        String key = StrUtil.join("", params);
        if (TIMED_CACHE.containsKey(key)) {
            return (JSONObject) TIMED_CACHE.get(key);
        }

        //调用api接口
        String newUrl = StrUtil.format(url, params);
        HttpResponse res = HttpRequest.get(newUrl).timeout(TIMEOUT).execute();
        if (res.isOk()) {
            String body = res.body();
            jsonObject = new JSONObject(body);
            if (inverse) {
                jsonObject = new JSONObject(MapUtil.inverse(jsonObject));
            }
            if (cache) {
                TIMED_CACHE.put(key, jsonObject, 0);
            }
            return jsonObject;
        }
        jsonObject.set(STATUS, ERROR_CODE);
        jsonObject.set("info", "请求有误");
        return jsonObject;
    }

    @Recover
    public Optional<Integer> recover(Exception e) {
        log.error("方法调用异常，{}", e.getMessage());
        return Optional.of(ERROR_CODE);
    }


}
